/*
    作者：zx
    时间：2022.11.11
    描述：字符串和内存相关操作函数实现
*/


#include "comm/types.h"
#include "tools/klib.h"
#include "tools/log.h"
#include "comm/cpu_instr.h"
// 字符串复制  src -> dest
void kernel_strcpy(char * dest, const char * src)
{
    if(!dest || !src) return;
    while(*src)
    {
        *dest++ = *src++;
    }
    *dest = '\0';
}

// 字符串复制size个字符  src -> dest
void kernel_strncpy(char * dest, const char * src, int size)
{
    if(!dest || !src || !size) return;

    char * d = dest;
    const char * s = src;
    while(size-- > 0 && (*s))
    {
        *d++ = *s++;
    }

    if(size == 0)
    {
        *(d-1) = '\0';
    }
    else
    {
        *d = '\0';
    }
    
}

// 字符串比较
int kernel_strcmp(const char * s1, const char * s2)
{
    if(!s1 || !s2) return -1;

    while (*s1 && *s2 && (*s1==*s2))
    {
        s1++;s2++;
    }
    return !(*s1 == '\0');

}
// 字符串比较size个字符
int kernel_strncmp(const char * s1, const char * s2, int size)
{
    if(!s1 || !s2) return -1;

    while (*s1 && *s2 && (*s1==*s2) &&size--)
    {
        s1++;s2++;
    }
    return !((*s1=='\0') || (*s2=='\0') || (*s1==*s2));
}

// 字符串长度
int kernel_strlen(const char * s)
{
    if(!s)
    {
        return 0;
    }
    const char *c = s;
    int size = 0;
    while (*c++)
    {
        size++;
    }
    return size;
}
     
// 内存复制
void kernel_memcpy(void * desc, void * src, int size)
{
    if(!desc || !src || !size)
    {
        return;
    } 
    uint8_t *d = desc;
    uint8_t *s = src;
    while(size--)
    {
        *d++ = *s++;
    }
}

// 内存设置
void kernel_memset(void * dest, uint8_t v, int size)
{
    if(!dest || !size)
    {
        return;
    } 
    uint8_t * d = dest;
    while(size--)
    {
        *d++ = v;
    } 
}
// 内存比较,一样返回0
int kernel_memcmp(void * dest1, void * dest2, int size)
{
    if(!dest1 || !dest2 || !size)
    {
        return -1;
    } 
    uint8_t *d1 = dest1;
    uint8_t *d2 = dest2;
    while(size--)
    {
        if(*d1++ != *d2++)
        {
            return 1;
        }
    }
    return 0;
}

void kernel_sprintf(char * buff, const char * fmt, ...)
{
    va_list args;
    va_start(args, fmt);

    kernel_vsprintf(buff, fmt, args);
    va_end(args);
}

void kernel_itoa(char * buf, int num, int base) 
{
     // 转换字符索引[-15, -14, ...-1, 0, 1, ...., 14, 15]
    static const char * num2ch = {"FEDCBA9876543210123456789ABCDEF"};
    char * p = buf;
    int old_num = num;

    // 仅支持部分进制
    if ((base != 2) && (base != 8) && (base != 10) && (base != 16)) {
        *p = '\0';
        return;
    }

    // 只支持十进制负数 
    int signed_num = 0;
    if ((num < 0) && (base == 10)) {
        *p++ = '-';
        signed_num = 1;
    }

  // 以下要对正负进行分开处理。对于十六进制数，不支持负数，所以需要将其转换成无符号数进行除和求余。原有的代码是按负数进行除和求余，导致转换结果不对。
    if (signed_num) {
        do {
            char ch = num2ch[num % base + 15];
            *p++ = ch;
            num /= base;
        } while (num);
    } else {
        uint32_t u_num = (uint32_t)num;
        do {
            char ch = num2ch[u_num % base + 15];
            *p++ = ch;
            u_num /= base;
        } while (u_num);
    }
    *p-- = '\0';

    // 将转换结果逆序，生成最终的结果
    char * start = (!signed_num) ? buf : buf + 1;
    while (start < p) {
        char ch = *start;
        *start = *p;
        *p-- = ch;
        start++;
    }
}

void kernel_vsprintf(void * src, const char * fmt, va_list args)
{   
    enum {NORMAL,READ_FMT} status = NORMAL;
    char *s = src;
    char ch;
    while( (ch=*fmt++))
    {
        if(status == NORMAL)
        {
            if(ch == '%')
            {
                status = READ_FMT;
            }
            else
            {
                *s++ = ch;
            }
        }
        else if(status == READ_FMT)
        {
            // 这里代码太长了，需要解耦，后面有时间再做
            if(ch == 's')
            {
                // 取出va_list中的参数
                const char * str = va_arg(args, char *);
                int len = kernel_strlen(str);
                while(len--) *s++ = *str++;
            }
            else if(ch == 'd')
            {
                int num = va_arg(args, int);
                // 10进制转字符串
                kernel_itoa(s, num , 10);
                s += kernel_strlen(s);
            }
            else if(ch == 'x')
            {
                int num = va_arg(args, int);
                // 16进制转字符串
                kernel_itoa(s, num , 16);
                s += kernel_strlen(s);
            }
            else if(ch == 'c')
            {
                char c = va_arg(args, int);
                *s++ = c;
            }

            status = NORMAL;
        }
    }
}

void pannic(const char * file, const int line, const char * func, const char * cond)
{
    log_printf("assert failed! %s", cond);
    log_printf("file: %s\nline: %d\nfunc: %s",file,line,func);
    hlt();
}

char * get_file_name_from_path(char * file_path)
{
    char * s = file_path;
    while (*s != '\0')
    {
        s++;
    }

    while(s >= file_path && *s != '/' && *s != '\\')
    {
        s--;
    }
    return s+1;
}

int get_strings_count(char ** start)
{
    int count = 0;
    if(start)
    {
        while(*start++)
        {
            count++;
        }
    }
    return count;
}
